// class Test {
//   constructor(key) {
//     this.key = key;
//   }
//   hello() {
//     console.log(`hello ${this.val}`);
//   }
// }
// const test = new Test();
// console.log(test.constructor === Test);
// console.log(test.__proto__ === test.constructor.prototype);
// function Test2(key, val) {
//   new Test(key);
//   this.val = val;
// }
// const test2 = new Test2(1, 2);
// Test2.prototype.__proto__ = Test.prototype;
// console.log(test2);
// console.log(test2.key);
// test2.hello();
// console.log(Object.create(Test2).__proto__);
// console.log(Test2);

// 原型链的一些小细分
// a=Object.create(b) a->b->Object.prototype=>null
// a={__proto__:b} a->b->Object.prototype=>null
// A extends B A.prototype->B.prototype->Object.prototype=>null
// const a=new A() a->A.prototype->B.prototype->Object.prototype=>null
// Object.setPrototypeOf(a,b) a->b->Object.prototype->null

// 警告： Object.prototype.__proto__ 访问器是非标准的，且已被弃用。你应该几乎总是使用 Object.setPrototypeOf。
// JavaScript 中的所有构造函数都有一个被称为 prototype 的特殊属性，它与 new 运算符一起使用。对原型对象的引用被复制到新实例的内部属性 [[Prototype]] 中。
// new相当于复制原型对象的引用到新实例的内部属性 [[Prototype]] 中并且复制构造函数内变量到新实例上为属性，extends相当于复制构造函数B的原型对象的引用到构造函数A的内部属性 [[Prototype]] 中。
// 一大误区是原型链是在原型即__proto__上遍历寻找直到null，引入了prototype作为原型对象后使new,extends等关键字能够构造对象和继承
// 但是不是继承链上一定要有A.prototype这种表示，不要本末倒置，从原型链上找也不要只想着prototype，有prototype只证明原型链上存在构造函数，需要prototype来承载他的属性
// 仅仅只有普通对象的原型链也是可以有的，不一定要有构造函数。

// 继承的七种实现方式
// 梳理思路来自https://juejin.cn/post/7206700328811790392#heading-5

// 1.原型链式继承
// function Parent() {
//   this.name = "jack";
//   this.list = ["A", "B"];
// }

// function Child() {}

// Child.prototype = new Parent();

// const child1 = new Child();
// child1.name = "tom";
// child1.list.push("C");
// console.log(child1.name);
// console.log(child1.list);

// const child2 = new Child();
// child2.list.push("D");
// console.log(child2.name);
// console.log(child2.list);
// 优点简单,可重用方法,方法自动更新
// 缺点 1.引用类型的变量会共用，上面展示了 2.无法向父类传参 3.无法实现多继承

// 2.构造函数式继承

// function Parent() {
//   this.name = "jack";
//   this.list = ["A", "B"];
// }

// function Child() {
//   Parent.call(this);
// }

// const child1 = new Child();
// console.log(child1.name);
// console.log(child1.list);

// 优点简单 1.不会共用引用类型变量 2.可以向父类传参 3.可以实现多继承
// 缺点 1.每次构造都会调用构造函数，导致父类方法不能复用 2.父类方法更新不会同步到子类实例 3.无法访问父类原型上的属性和方法

// 3.组合式继承

// function Parent() {
//   this.name = "jack";
//   this.list = ["A", "B"];
//   console.log("used");
// }

// function Child(age) {
//   Parent.call(this);
//   this.age = age;
// }
// // new的作用分为两部分，一部分为新建一个当前构造函数的实例，并把对应的属性绑定到实例上；另一部分为构建原型链，把构造函数的prototype传到实例的__proto__上
// // 给Child.prototype赋值会覆盖掉原来的构造函数属性，所以要重新绑定构造函数，提升代码健壮性
// Child.prototype = new Parent();
// Child.prototype.constructor = Child;

// const child1 = new Child(14);
// console.log(child1.age);

// 优点 结合了原型链和构造函数式两者优点 1.子类实例可以访问父类上的方法,可以重用父类的方法 2.可以向父类传参且不会影响其他实例 3.可以实现多继承 4.简单易懂
// 缺点 1.每次会调用两次父类构造函数，影响性能  2.子类实例会同时拥有自己的属性和方法，以及父类的属性和方法，可能导致内存浪费和属性名冲突的问题。
// 因为Child.prototype = new Parent()的时候就会调用一次，后续实例化Child的时候又会调用一次

// 4.原型式继承

// const person = {
//   name: "jack",
//   list: ["A", "B"],
//   sayHello() {
//     console.log(`hello i am ${this.name}`);
//   },
// };

// const tommy = Object.create(person);
// tommy.name = "tommy";
// tommy.list.push("C");
// tommy.sayHello();
// console.log(tommy.list);

// const tony = Object.create(person);
// tony.name = "tony";
// tony.list.push("D");
// tony.sayHello();
// console.log(tony.list);

// 优点 1.特别简单 2.可以实现对象复用
// 缺点 1.会共用引用类型 2.无法判断是否是父对象的实例 3.无法实现多继承

// 5.寄生式继承

// const person = {
//   name: "jack",
//   list: ["A", "B"],
//   sayHello() {
//     console.log(`hello i am ${this.name}`);
//   },
// };

// function createChild(parent) {
//   let child = Object.create(parent);
//   child.smile = () => console.log("ha ha");
//   return child;
// }
// const child = createChild(person);
// child.name = "eric";
// console.log(child.name);
// child.smile();

// 优点 1.简单、易于理解和实现 2.可以基于一个对象创建多个对象，实现对象复用 3.可以在不修改原对象的情况下，对继承过程进行一些增强，例如添加新的属性和方法
// 缺点 1.父对象的引用属性会被所有子对象共享，因此子对象的修改会影响到其他子对象 2.子对象无法像传统的类继承一样判断自己是否是父对象的实例。
// 3.增强行为可能会带来一定的性能开销 4.可能会导致代码的可读性降低

// 6.寄生式组合继承

// function Parent() {
//   this.name = "jack";
//   this.list = ["A", "B"];
//   console.log("used");
// }

// function Child(age) {
//   Parent.call(this);
//   this.age = age;
// }
// Child.prototype = Object.create(Parent.prototype);
// Child.prototype.constructor = Child;

// const child1 = new Child(14);
// console.log(child1.age);

// 优点 1.实现了属性和方法的完整继承 2.避免了组合继承中重复调用父类构造函数的问题，提高了性能 3.可以在不修改原对象的情况下，对继承过程进行一些增强，例如添加新的属性和方法。
// 缺点 1.增加了一层包装函数，可能会带来一定的性能开销 2.可能会导致代码的可读性降低
// 总体来说几乎没有明显的缺点,就是近乎完美的继承方式

// 7.class继承

// class Parent {
//   constructor() {
//     this.name = "jack";
//     this.list = ["A", "B"];
//   }
// }
// class Child extends Parent {
//   constructor(age) {
//     super();
//     this.age = age;
//   }
// }

// const child1 = new Child(5);
// child1.list.push("C");
// console.log(child1.list);
// console.log(child1.age);

// const child2 = new Child(6);
// child2.list.push("D");
// console.log(child2.list);
// console.log(child2.age);

// 优点 1.代码可读性高，更易于理解和维护 2.语法简洁，可以更快地编写代码 3.可以使用现代JavaScript特性，如箭头函数、解构赋值等。
// 缺点 1.与ES5及以下版本的JavaScript不兼容 2.需要编译才能运行在低版本浏览器中 3.某些开发者可能认为使用类和继承违背了JavaScript的本质。
